/*** 
 * @Author: Leung
 * @Date: 2025-07-21 19:51:55
 * @LastEditTime: 2025-07-25 22:13:48
 * @FilePath: \Projecte:\STM32F103ZET6\Peripheral modules\EEPROM\I2C_Soft\User\i2c\bsp_i2c_gpio.h
 * @Description: 
 * @
 * @Copyright (c) 2025 by ${git_name_email}, All Rights Reserved. 
 */
#ifndef __I2C_GPIP_H
#define __I2C_GPIP_H

#include <inttypes.h>

#define EEPROM_I2C_WR   0       // 写控制bit
#define EEPROM_I2C_RD   1       // 读控制bit

/**
 * 核心思想：我们放弃MCU内置的I2C硬件外设，选择任意两个GPIO引脚（例如 `PB6` 作为SCL，`PB7` 作为SDA），
 * 用软件程序去精确地控制这两个引脚的电平高低和时序，从而完整地模拟出I2C协议的物理层行为。
 * 因此，需定义两个引脚GPIO来模拟I2C的SCL和SDA总线
 */
#define EEPROM_I2C_GPIO_PORT    GPIOB       // GPIO端口
#define EEPROM_I2C_GPIO_CLK     RCC_APB2Periph_GPIOB        // 定义对应引脚的外设
#define EEPROM_I2C_SCL_PIN      GPIO_Pin_6  // SCL时钟线连接到GPIO的PB6引脚
#define EEPROM_I2C_SDA_PIN      GPIO_Pin_7  // SDA时钟线连接到GPIO的PB7引脚

/**
 * 定义读写SCL和SDA的宏，以增加代码的可移植性的可阅读性
 * 0：选择GPIO的库函数实现IO读写
 * 1：选择直接寄存器操作实现IO读写
 */
#if 0
    #define SCL_1()  GPIO_SetBits(EEPROM_I2C_GPIO_PORT, EEPROM_I2C_SCL_PIN)		/* SCL = 1 */
	#define SCL_0()  GPIO_ResetBits(EEPROM_I2C_GPIO_PORT, EEPROM_I2C_SCL_PIN)	/* SCL = 0 */
	
	#define SDA_1()  GPIO_SetBits(EEPROM_I2C_GPIO_PORT, EEPROM_I2C_SDA_PIN)		/* SDA = 1 */
	#define SDA_0()  GPIO_ResetBits(EEPROM_I2C_GPIO_PORT, EEPROM_I2C_SDA_PIN)	/* SDA = 0 */
	
	#define SDA_READ()  GPIO_ReadInputDataBit(EEPROM_I2C_GPIO_PORT, EEPROM_I2C_SDA_PIN)	/* 读SDA口线状态 */
#else
    /* 注意：以下写法在IAR最高级别优化时，会被编译器错误优化 */
    #define SCL_1()  EEPROM_I2C_GPIO_PORT->BSRR	= EEPROM_I2C_SCL_PIN	/* SCL = 1 */
	#define SCL_0()  EEPROM_I2C_GPIO_PORT->BRR	= EEPROM_I2C_SCL_PIN	/* SCL = 0 */
	
	#define SDA_1()  EEPROM_I2C_GPIO_PORT->BSRR	= EEPROM_I2C_SDA_PIN		/* SDA = 1 */
	#define SDA_0()  EEPROM_I2C_GPIO_PORT->BRR	= EEPROM_I2C_SDA_PIN	/* SDA = 0 */
	
	#define SDA_READ()  ((EEPROM_I2C_GPIO_PORT->IDR & EEPROM_I2C_SDA_PIN) != 0)	/* 读SDA口线状态 */
#endif

/* I2C协议核心函数 */
void i2c_Start(void);                   // 起始信号
void i2c_Stop(void);                    // 停止信号
void i2c_SendByte(uint8_t _ucByte);     // 发送一个字节
uint8_t i2c_ReadByte(void);             // 读取一个字节
uint8_t i2c_WaitAck(void);             // 等待应答信号
void i2c_Ack(void);                     // 发送应答
void i2c_NAck(void);                    // 发送非应答
uint8_t i2c_CheckDevice(uint8_t _Address);  // 监测设备地址

#endif /* __I2C_GPIP_H */
